package com.piggy.logi.sys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.piggy.common.core.constant.SecurityConstants;
import com.piggy.common.core.domain.R;
import com.piggy.common.core.exception.base.BaseException;
import com.piggy.common.core.utils.FeignResultUtils;
import com.piggy.common.core.utils.PageUtils;
import com.piggy.common.core.web.page.PagePlus;
import com.piggy.common.core.web.page.TableDataInfo;
import com.piggy.common.satoken.utils.SecurityUtils;
import com.piggy.logi.sys.api.bo.LogiPricePlanExtEditBo;
import com.piggy.logi.sys.api.bo.LogiPricePlanQueryBo;
import com.piggy.logi.sys.api.bo.LogiPriceSegEditBo;
import com.piggy.logi.sys.api.vo.LogiPricePlanExtVo;
import com.piggy.logi.sys.api.vo.LogiPriceSegExtVo;
import com.piggy.logi.sys.domain.LogiPricePlan;
import com.piggy.logi.sys.domain.LogiPriceSeg;
import com.piggy.logi.sys.enums.FeeTypeEnum;
import com.piggy.logi.sys.mapper.LogiPricePlanMapper;
import com.piggy.logi.sys.service.ILogiLineService;
import com.piggy.logi.sys.service.ILogiPricePlanService;
import com.piggy.logi.sys.service.ILogiPriceSegService;
import com.piggy.sys.api.RemoteDistrictService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 价格方案管理Service业务层处理
 *
 * @author piggy
 * @date 2023-08-14
 */
@Slf4j
@Service
public class LogiPricePlanServiceImpl extends ServiceImpl<LogiPricePlanMapper, LogiPricePlan> implements ILogiPricePlanService {

    @Resource
    private ILogiPriceSegService segService;

    @Resource
    private RemoteDistrictService districtService;

    @Resource
    private ILogiLineService lineService;

    @Override
    public LogiPricePlanExtVo queryById(Long id) {

        List<LogiPricePlanExtVo> voList = fillData(Lists.newArrayList(getVoById(id, LogiPricePlanExtVo.class)));
        if (CollUtil.isEmpty(voList)) {
            return null;
        }
        return voList.get(0);
    }

    @Override
    public TableDataInfo<LogiPricePlanExtVo> queryPageList(LogiPricePlanQueryBo bo) {
        PagePlus<LogiPricePlan, LogiPricePlanExtVo> result = pageVo(PageUtils.buildPagePlus(bo, true), buildQueryWrapper(bo), LogiPricePlanExtVo.class);
        return PageUtils.buildDataInfo(result.setRecordsVo(fillData(result.getRecordsVo())));
    }

    @Override
    public List<LogiPricePlanExtVo> queryList(LogiPricePlanQueryBo bo) {
        List<LogiPricePlanExtVo> voList = listVo(buildQueryWrapper(bo), LogiPricePlanExtVo.class);
        /*LogiPricePlanExtVo vo = getVoOne(Wrappers.<LogiPricePlan>lambdaQuery().eq(LogiPricePlan::getDeptId, bo.getDeptId())
                        .and(i -> i.eq(LogiPricePlan::getLineId, null).or().eq(LogiPricePlan::getLineId, ""))
                        .and(i -> i.eq(LogiPricePlan::getDistrictId, null).or().eq(LogiPricePlan::getDistrictId, ""))
                        .and(i -> i.eq(LogiPricePlan::getProjectId, null).or().eq(LogiPricePlan::getProjectId, 0))
                        .and(i -> i.eq(LogiPricePlan::getProjectShiplineId, null).or().eq(LogiPricePlan::getProjectShiplineId, 0))
                        .last("limit 1"),
                LogiPricePlanExtVo.class);
        if (ObjectUtil.isNotEmpty(vo)) {
            voList.add(vo);
        }*/
        return fillData(voList);
    }

    @Override
    public LogiPricePlanExtVo queryDistAndDefault(String desDist, Long deptId) {
        List<LogiPricePlanExtVo> voList = listVo(buildQueryWrapper((LogiPricePlanQueryBo) new LogiPricePlanQueryBo().setDesDist(desDist).setDeptId(deptId)),
                LogiPricePlanExtVo.class);
        LogiPricePlanExtVo vo = getVoOne(Wrappers.<LogiPricePlan>lambdaQuery().eq(LogiPricePlan::getDeptId, SecurityUtils.getMainDeptId())
                        .and(i -> i.eq(LogiPricePlan::getLineId, null).or().eq(LogiPricePlan::getLineId, ""))
                        .and(i -> i.eq(LogiPricePlan::getDistrictId, null).or().eq(LogiPricePlan::getDistrictId, ""))
                        .and(i -> i.eq(LogiPricePlan::getProjectId, null).or().eq(LogiPricePlan::getProjectId, 0))
                        .and(i -> i.eq(LogiPricePlan::getProjectShiplineId, null).or().eq(LogiPricePlan::getProjectShiplineId, 0))
                        .last("limit 1"),
                LogiPricePlanExtVo.class);
        if (ObjectUtil.isNotEmpty(vo)) {
            voList.add(vo);
        }
        if (CollUtil.isEmpty(voList)) {
            return null;
        }
        return fillData(Lists.newArrayList(voList.get(0))).get(0);
    }

    @Override
    public List<LogiPricePlanExtVo> queryByDist(String desDist) {

        if (StrUtil.isAllNotBlank(desDist)) {
            throw new BaseException("出发地或目的地区域不能为空");
        }
        LogiPricePlanQueryBo queryBo = new LogiPricePlanQueryBo();
        queryBo.setDeptId(SecurityUtils.getDeptId());
        queryBo.setDesDist(desDist);
        return fillData(listVo(buildQueryWrapper(queryBo), LogiPricePlanExtVo.class));

    }

    @Override
    public List<LogiPricePlanExtVo> queryByDistAndLine(String desDist, String andLine) {

        if (StrUtil.isAllNotBlank(desDist, andLine)) {
            throw new BaseException("出发地或目的地区域不能为空，线路不能为空");
        }

        LogiPricePlanQueryBo queryBo = new LogiPricePlanQueryBo();
        queryBo.setDeptId(SecurityUtils.getDeptId());
        queryBo.setAndLine(andLine);
        queryBo.setDesDist(desDist);
        return fillData(listVo(buildQueryWrapper(queryBo), LogiPricePlanExtVo.class));

    }

    @Override
    public List<LogiPricePlanExtVo> queryByAndLine(String andLine) {

        if (StrUtil.isNotBlank(andLine)) {
            throw new BaseException("出发地或目的地区域不能为空，线路不能为空");
        }
        LogiPricePlanQueryBo queryBo = new LogiPricePlanQueryBo();
        queryBo.setDeptId(SecurityUtils.getDeptId());
        queryBo.setAndLine(andLine);
        return fillData(listVo(buildQueryWrapper(queryBo), LogiPricePlanExtVo.class));

    }

    private List<LogiPricePlanExtVo> fillData(List<LogiPricePlanExtVo> extVoList1) {

        List<LogiPricePlanExtVo> extVoList = extVoList1.stream().peek(vo -> {

            if (StrUtil.isBlank(vo.getDistrictId())) {
                return;
            }

            if (StrUtil.isNotBlank(vo.getLineId())) {
                vo.setLineList(lineService.queryListByIds(Arrays.stream(vo.getLineId().split(",")).map(Long::parseLong).collect(Collectors.toList())));
            }

            if (StrUtil.isBlank(vo.getDistrictId())) {
                return;
            }

            List<String> distIds = Lists.newArrayList(vo.getDistrictId().split(","));
            R<Map<String, String>> distNameMap = districtService.getDistrictMap(distIds, SecurityConstants.INNER);
            FeignResultUtils.throwIfFailed(distNameMap);
            if (CollUtil.isEmpty(distNameMap.getData())) {
                return;
            }

            vo.setDistrictName(Joiner.on(",").join(ObjectUtil.defaultIfNull(distNameMap.getData(), Maps.newHashMap()).values()));
            distNameMap.getData().forEach((k, v) -> {
                LogiPricePlanExtVo.DistLineVo distLineVo = new LogiPricePlanExtVo.DistLineVo().setDistId(k).setDistName(v);
                distLineVo.setLineNum(lineService.getLineNums(k));
                vo.getDistLineList().add(distLineVo);
            });

        }).collect(Collectors.toList());

        LinkedHashMap<Long, LogiPricePlanExtVo> extVoMap = extVoList.stream().collect(LinkedHashMap::new, (m, v) -> m.put(v.getId(), v), LinkedHashMap::putAll);
        extVoMap.put(-1L, new LogiPricePlanExtVo());
        List<LogiPriceSegExtVo> segVos = segService.listVo(Wrappers.<LogiPriceSeg>lambdaQuery().in(LogiPriceSeg::getPlanId, extVoMap.keySet()).orderByAsc(LogiPriceSeg::getSort), LogiPriceSegExtVo.class);

        segVos.stream().peek(Any -> {
            LogiPricePlanExtVo vo = extVoMap.get(Any.getPlanId());
            vo.getSegList().add(Any);
        }).collect(Collectors.toList());

        extVoMap.remove(-1L);
        return Lists.newArrayList(extVoMap.values());
    }

    private LambdaQueryWrapper<LogiPricePlan> buildQueryWrapper(LogiPricePlanQueryBo bo) {
        LambdaQueryWrapper<LogiPricePlan> lqw = Wrappers.lambdaQuery();
        lqw.and(i->i.eq(LogiPricePlan::getDeptId, 0L).or().eq(ObjectUtil.isNotEmpty(bo.getDeptId()), LogiPricePlan::getDeptId, bo.getDeptId()));
        //lqw.eq(ObjectUtil.isNotEmpty(bo.getDeptId()), LogiPricePlan::getDeptId, bo.getDeptId());
        lqw.eq(bo.getId() != null, LogiPricePlan::getId, bo.getId());
        lqw.eq(ObjectUtil.isNotEmpty(bo.getProjectId()), LogiPricePlan::getProjectId, bo.getProjectId());
        lqw.eq(ObjectUtil.isNotEmpty(bo.getProjectShiplineId()), LogiPricePlan::getProjectShiplineId, bo.getProjectShiplineId());
        lqw.like(StrUtil.isNotBlank(bo.getPlanName()), LogiPricePlan::getPlanName, bo.getPlanName());
        lqw.eq(StrUtil.isNotBlank(bo.getTransportType()), LogiPricePlan::getTransportType, bo.getTransportType());
        lqw.eq(StrUtil.isNotBlank(bo.getFeeMode()), LogiPricePlan::getFeeMode, bo.getFeeMode());
        lqw.eq(StrUtil.isNotBlank(bo.getAddFeeMode()), LogiPricePlan::getAddFeeMode, bo.getAddFeeMode());
        lqw.eq(StrUtil.isNotBlank(bo.getAddFeeMode1()), LogiPricePlan::getAddFeeMode1, bo.getAddFeeMode1());
        lqw.eq(StrUtil.isNotBlank(bo.getDistrictId()), LogiPricePlan::getDistrictId, bo.getDistrictId());
        lqw.eq(StrUtil.isNotBlank(bo.getStatus()), LogiPricePlan::getStatus, bo.getStatus());
        lqw.in(CollUtil.isNotEmpty(bo.getIdList()), LogiPricePlan::getId, bo.getIdList());
        lqw.apply(ObjectUtil.isNotEmpty(bo.getDeptId()) && bo.getDeptId()>0L, " (id not in (select id from logi_price_plan where dept_id=0 and fee_mode in (select distinct fee_mode from logi_price_plan where dept_id=" + bo.getDeptId() + "))) ");

        if (StrUtil.isNotBlank(bo.getLineId())) {
            String[] lineIds = bo.getLineId().split(",");
            String findSQL = "";
            for (String line : lineIds) {
                findSQL += StrUtil.isBlank(line) ? "" : (StrUtil.isBlank(findSQL) ? " FIND_IN_SET ('" + line + "',line_id)" : " OR FIND_IN_SET ('" + line + "',line_id)");
            }
            lqw = lqw.apply(StrUtil.isNotBlank(findSQL), "(" + findSQL + ")");
        }

        if (StrUtil.isAllNotBlank(bo.getDesDist())) {
            String findSQL = " FIND_IN_SET ('" + bo.getDesDist() + "',district_id) ";
            lqw = lqw.apply(StrUtil.isNotBlank(findSQL), "(" + findSQL + ")");
        }

        if (StrUtil.isNotBlank(bo.getAndLine())) {
            String[] lineIds = bo.getLineId().split(",");
            String findSQL = "";
            for (String line : lineIds) {
                findSQL += StrUtil.isBlank(line) ? "" : (StrUtil.isBlank(findSQL) ? " FIND_IN_SET ('" + line + "',line_id)" : " AND FIND_IN_SET ('" + line + "',line_id)");
            }
            lqw = lqw.apply(StrUtil.isNotBlank(findSQL), "(" + findSQL + ")");
        }

        return lqw;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LogiPricePlanExtVo insertByAddBo(LogiPricePlanExtEditBo bo) {
        //bo.setDeptId(SecurityUtils.getMainDeptId());
        bo.setId(null);
        LogiPricePlan add = BeanUtil.toBean(bo, LogiPricePlan.class);
        save(add);
        bo.setSegList(bo.getSegList().stream().peek(Any -> {
            Any.setPlanId(add.getId());
            Any.setId(null);
        }).collect(Collectors.toList()));
        if (FeeTypeEnum.isWeightMultiMileage(bo.getFeeMode())) {
            LogiPriceSegEditBo segBo = new LogiPriceSegEditBo().setPlanId(add.getId()).setFloatStart(BigDecimal.valueOf(bo.getWaterProof()));
            segBo.setSort(bo.getSegList().size()).setFloatEnd(BigDecimal.ZERO).setPrice(BigDecimal.ZERO);
            segBo.setFeeMode(FeeTypeEnum.WEIGHT.getType());
            bo.getSegList().add(segBo);
        }

        segService.batchByAddBo(bo.getSegList());
        return queryById(add.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LogiPricePlanExtVo updateByEditBo(LogiPricePlanExtEditBo bo) {
        /*if (ObjectUtil.isEmpty(bo.getDeptId()) || bo.getDeptId()==0) {
            return insertByAddBo(bo);
        }*/
        //bo.setDeptId(SecurityUtils.getDeptId());

        LogiPricePlan update = BeanUtil.toBean(bo, LogiPricePlan.class);
        updateById(update);
        update = getById(update.getId());
        LogiPricePlan finalUpdate = update;
        bo.setSegList(bo.getSegList().stream().peek(Any -> {
            Any.setPlanId(finalUpdate.getId());
            Any.setId(null);
        }).collect(Collectors.toList()));
        if (FeeTypeEnum.isWeightMultiMileage(update.getFeeMode())) {
            LogiPriceSegEditBo segBo = new LogiPriceSegEditBo().setPlanId(update.getId()).setFloatStart(BigDecimal.valueOf(bo.getWaterProof()));
            segBo.setSort(bo.getSegList().size()).setFloatEnd(BigDecimal.ZERO).setPrice(BigDecimal.ZERO);
            segBo.setFeeMode(FeeTypeEnum.WEIGHT.getType());
            bo.getSegList().add(segBo);
        }

        segService.deleteByPlanId(bo.getId());
        segService.batchByAddBo(bo.getSegList());
        return queryById(update.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return removeByIds(ids);
    }

    @Override
    public BigDecimal calcAmountFee(Long planId, BigDecimal amount) {
        return segService.calcAmountFee(planId, amount);
    }

    @Override
    public BigDecimal calcMileFee(Long planId, BigDecimal miles) {
        return segService.calcMileFee(planId, miles);
    }

    @Override
    public BigDecimal calcWeightAndVolFee(Long planId, BigDecimal weight, BigDecimal vol) {
        return segService.calcWeightAndVolFee(planId, weight, vol);
    }

    @Override
    public BigDecimal calcWeigthMultiMileFee(Long planId, BigDecimal weight, BigDecimal miles) {
        return segService.calcWeigthMultiMileFee(planId, weight, miles);
    }

}
